package handledb;

import java.util.List;

public class SqlResolver {

    private static char[] left = new char[]{'{', '(', '\'', '`', '"'};
    private static char[] right = new char[]{'}', ')', '\'', '`', '"'};

    private static int exist(char[] cs, char arg) {
        for (int i = 0; i < cs.length; i++) {
            if (cs[i] == arg) {
                return i;
            }
        }
        return -1;
    }

    public static <T> String templateResolver(String temp, T t, List<Object> params, ClassInfo classInfo) {
        char[] arrs = temp.toCharArray();
        char[] sqlChars = new char[arrs.length];
        char[] newSqlChars = null;
        char[] fieldChars = new char[256];
        int sqlLen = 0;
        int fieldLen = 0;

        boolean hasAdd = true;
        boolean hasField = false;   // 是否是字段
        boolean hasIf = false;      // 是否是if
        boolean hasWhere = false;   // 是否是where 条件语句
        boolean hasNext = false;
        char matchChar = 0;
        int index = 0;
        StringBuffer sql = new StringBuffer();
        char c;
        Object param = null;
        ResolverInfo[] queue = new ResolverInfo[20];
        int queueIndex = 0;
        ResolverInfo resolverInfo = new ResolverInfo(0);
        ResolverInfo tempInfo = null;
        queue[queueIndex++] = resolverInfo;
        String fieldName;
        FieldInfo fieldInfo;
        for (int i = 0; i < arrs.length; i++) {
            c = arrs[i];
            // System.out.println("hasField: "+hasField+"  index: "+index+"   "+c);
            if (!hasField && index < 2) { // ‘ “ `不在查左边界
                if (c == '#') { // 字段匹配 #{字段名}
                    queue[queueIndex++] = resolverInfo = new ResolverInfo(1);
                    fieldLen = 0;
                    matchChar = '}';
                    resolverInfo.matchChar = matchChar;
                    resolverInfo.matchIndex = 0;
                    i++;
                    hasField = true;
                    hasAdd = false;
                    continue;
                }
                if (c == 'w' && arrs[i + 1] == 'h' && arrs[i + 2] == 'e' && arrs[i + 3] == 'r' && arrs[i + 4] == 'e') {
                    hasWhere = true;
                    //System.out.println("matchchar: "+matchChar+ "      "+resolverInfo.matchChar+"  i: "+i+"   size: "+arrs.length);
                    resolverInfo.hasWhere = true;
                    resolverInfo.whereIndex = sqlLen + 4;
                }
                if (c == 'i' && arrs[i + 1] == 'f' && arrs[i + 2] == '(') {
                    hasIf = true;
                    hasAdd = false;
                    fieldLen = 0;
                    matchChar = ')';
                    queue[queueIndex++] = resolverInfo = new ResolverInfo(2);
                    resolverInfo.matchChar = matchChar;
                    i += 2;
                    continue;
                }
                index = exist(left, c);
                if (index > -1) {
                    matchChar = right[index];
                    queue[queueIndex++] = resolverInfo = new ResolverInfo(0);
                    resolverInfo.matchChar = matchChar;
                    resolverInfo.startIndex = sqlLen;
                    resolverInfo.matchIndex = index;
                    if (hasWhere && matchChar == ')') {
                        resolverInfo.hasWhere = true;
                    }
                    if (index > 1) {
                        sqlChars[sqlLen++] = c;
                        continue;
                    }
                }
            }
            if (matchChar == c) {
                queueIndex--;
                tempInfo = queue[queueIndex - 1];
                if (resolverInfo.type > 0) {
                    hasAdd = true;
                }
                if (hasField) {
                    hasField = false;
                    try {
                        fieldInfo = null;
                        fieldName = charToString(fieldChars, fieldLen);
                        for (FieldInfo info : classInfo.allFieldInfos) {
                            if (fieldName.equals(info.name)) {
                                param = info.field.get(t);
                                fieldInfo = info;
                                break;
                            }
                        }
                        if (fieldInfo == null) {
                            throw new RuntimeException("字段【" + fieldName + "】不是类" + classInfo.beanName + "的属性！");
                        }
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                    //System.out.println("haswhere   :"+hasWhere+"-----");
                    if (hasWhere) {
                        if (param == null) {
                            // 去掉查询条件
                            sqlLen = backDealWhere(sqlChars, sqlLen);
                        } else {
                            if (fieldInfo.type < 20) {
                                sqlChars[sqlLen++] = '?';
                                params.add(param);
                                if (tempInfo.conditionNum == 0) {
                                    //System.out.println("  field   "+c+"  ==========");
                                    sqlLen = backDealCondition(sqlChars, sqlLen, tempInfo, false);
                                }
                            } else {
                                Object[] ps = null;
                                if (fieldInfo.type == 100) {
                                    ps = ((List) param).toArray();
                                } else {
                                    ps = (Object[]) param;
                                }
                                //System.out.println("ps: "+ps.length+" sqllen: "+sqlLen+"  "+queue[queueIndex-1].matchChar);
                                if (ps.length < 1) {
                                    sqlLen = backDealWhere(sqlChars, sqlLen);
                                } else {
                                    if (sqlLen + ps.length * 2 > i) { // 扩容
                                        newSqlChars = new char[ps.length * 2 + arrs.length];
                                        for (int j = 0; j < sqlLen; j++) {
                                            newSqlChars[j] = sqlChars[j];
                                        }
                                        sqlChars = newSqlChars;
                                    }
                                    for (Object obj : ps) {
                                        sqlChars[sqlLen++] = '?';
                                        sqlChars[sqlLen++] = ',';
                                        params.add(obj);
                                    }
                                    sqlLen--;
                                    if (tempInfo.conditionNum == 0) {
                                        //System.out.println("  field   "+c+"  ==========");
                                        sqlLen = backDealCondition(sqlChars, sqlLen, tempInfo, false);
                                    }
                                }
                            }
                        }
                    } else {
                        sqlChars[sqlLen++] = '?';
                        params.add(param);
                    }
                } else {
                    if (resolverInfo.type == 2) { // if结束

                    } else if (resolverInfo.type == 3) { // else结束

                    } else {
                        hasNext = true;
                        if (resolverInfo.hasWhere && resolverInfo.conditionNum == 0) {
                            //System.out.println("  other   "+c+"   -"+sqlChars[resolverInfo.startIndex]+"---"+resolverInfo.conditionNum+"--------" + charToString(sqlChars,sqlLen));
                            sqlLen = backDealCondition(sqlChars, sqlLen, resolverInfo, c == ')');
                            hasNext = sqlChars[resolverInfo.startIndex] == '(';
                        }
                    }
                }
                resolverInfo = queue[queueIndex - 1];
                matchChar = resolverInfo.matchChar;
                hasWhere = resolverInfo.hasWhere;
                if (hasNext) {
                    hasNext = false;
                    sqlChars[sqlLen++] = c;
                }
                index = resolverInfo.matchIndex;
                //System.out.println(resolverInfo.matchChar+"  "+resolverInfo.conditionNum+"  i: "+i+ " c:"+c+" sqlLen: "+sqlLen);
            } else if (hasAdd) {
                sqlChars[sqlLen++] = c;
            } else if (hasField || hasIf) {
                fieldChars[fieldLen++] = c;
            }
        }
        if (resolverInfo.type == 0 && resolverInfo.hasWhere && resolverInfo.conditionNum == 0) {
            sqlLen = backDealCondition(sqlChars, sqlLen, resolverInfo, true);
        }
        return charToString(sqlChars, sqlLen);
    }

    // where or code=? and id=? 这种情况去掉or 剩下 where code=? and id=?
    private static int backDealCondition(char[] cs, int len, ResolverInfo resolverInfo, boolean endWhere) {
        int i = resolverInfo.whereIndex + 1;
        int n = 0;
        char c = 0;
        int end = len - 1;
        for (; i < len; i++) {
            c = cs[i];
            if (c == ' ' && cs[i + 1] == 'o' && cs[i + 2] == 'r' && cs[i + 3] == ' ') {
                n = 3;
                break;
            } else if (c == ' ' && cs[i + 1] == 'a' && cs[i + 2] == 'n' && cs[i + 3] == 'd' && cs[i + 4] == ' ') {
                n = 4;
                break;
            } else if (c != ' ') {
                resolverInfo.conditionNum++;
                return len;
            } else if (endWhere && i == end) {
                return resolverInfo.whereIndex - 5;
            }
        }
        if (n == 0 && endWhere) {
            return resolverInfo.whereIndex - 5;
        }
        i += n;
        for (; i < len; i++) {
            cs[i - n] = cs[i];
        }
        return len - n;
    }

    // 处理条件忽略 where name=? and code=#{code}  code=null去掉条件code=?  剩下 where name=?
    private static int backDealWhere(char[] cs, int len) {
        int i = len - 1;
        for (; i > -1; i--) {
            if (cs[i] == ' ') {
                if (cs[i - 1] == 'r' && cs[i - 2] == 'o' && cs[i - 3] == ' ') {
                    cs[i] = 0;
                    cs[i - 1] = 0;
                    cs[i - 2] = 0;
                    cs[i - 3] = 0;
                    return i - 3;
                } else if (cs[i - 1] == 'd' && cs[i - 2] == 'n' && cs[i - 3] == 'a' && cs[i - 4] == ' ') {
                    cs[i] = 0;
                    cs[i - 1] = 0;
                    cs[i - 2] = 0;
                    cs[i - 3] = 0;
                    cs[i - 4] = 0;
                    return i - 4;
                } else if (cs[i - 1] == 'e' && cs[i - 2] == 'r' && cs[i - 3] == 'e' && cs[i - 4] == 'h' && cs[i - 5] == 'w' && cs[i - 6] == ' ') {
                    for (int j = i + 1; j < len; j++) {
                        cs[j] = 0;
                    }
                    return i;
                }
            }
            cs[i] = 0;
        }
        return 0;
    }

    private static String charToString(char[] cs, int len) {
        char[] arr = new char[len];
        for (int i = 0; i < len; i++) {
            arr[i] = cs[i];
        }
        return new String(arr);
    }

    private static <T> Object getParam(T t, ClassInfo classInfo, String fieldName) {
        try {
            for (FieldInfo info : classInfo.allFieldInfos) {
                if (fieldName.equals(info.name)) {
                    return info.field.get(t);
                }
            }
            throw new RuntimeException("字符【" + fieldName + "】不是类" + classInfo.beanName + "的属性！");
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

}

class ResolverInfo {
    public char matchChar; // 匹配的字符
    public int matchIndex;
    public int type = 0; // 0 其它、 1 字段、2 if、3 else
    public boolean hasLose = false; // 是否可以丢弃  为true将不会处理子节点和本节点
    public boolean hasWhere = false; // 是否是where条件语句
    public int whereIndex = 0;
    public int conditionNum = 0;
    public int startIndex = 0;

    public ResolverInfo(int t) {
        this.type = t;
    }
}